﻿using System;
using System.Collections.Generic;
using System.Text;

namespace BioCompEx2
{
    class SolutionSet
    {
        private int populationSize;
        private Solution[] solutions;
        private Solution[] newSols;
        private int newIndex;
        private int topGrade;
        private int lowestGrade;
        private String bestDecodedSTR;
        private Solution bestSolution;
        private int bestGrade;
        private float averageGrade;
        public static Random rand = new Random(DateTime.Now.Hour + DateTime.Now.Minute + DateTime.Now.Second + DateTime.Now.Millisecond);
        private double Pc;
        private double Pm;
        private double originalPm;

        public int TopGrade
        {
            get
            {
                return topGrade;
            }
        }

        public int LowestGrade
        {
            get
            {
                return lowestGrade;
            }
        }

        public float AverageGrade
        {
            get
            {
                return averageGrade;
            }
        }

        public String BestDecodedSTR
        {
            get
            {
                return bestDecodedSTR;
            }
        }

        public int BestGrade
        {
            get
            {
                return bestGrade;
            }
        }

        public double PM
        {
            set
            {
                Pm = value;
            }
        }

        public void resetPm()
        {
            Pm = originalPm;
        }

        //CTOR - gets the default parameters, creates the population and updates the gardes
        public SolutionSet(double mutationProbability, double crossoverProbability, int populationSize)
        {
            bestGrade = 0;
            Pm = mutationProbability/100;
            originalPm = Pm;
            Pc = crossoverProbability/100;
            this.populationSize = populationSize;
            solutions = new Solution[populationSize];
            for (int i = 0; i < solutions.Length; i++)
            {
                solutions[i] = new Solution();
            }
            updateGrades();
        }

        //update the top most grade, buttom most grade, average garde,
        //and when needed the best grade, solution and decoded text
        private void updateGrades()
        {
            topGrade = 0;
            lowestGrade = DataForm.maxGrade;
            averageGrade = 0;
            foreach (Solution sol in solutions)
            {
                averageGrade += sol.Grade;
                if (sol.Grade > topGrade)
                {
                    topGrade = sol.Grade;
                    if (topGrade >= bestGrade)
                    {
                        bestGrade = topGrade;
                        bestSolution = sol;
                        bestDecodedSTR = sol.DecodedText;
                    }
                }
                if (sol.Grade < lowestGrade)
                    lowestGrade = sol.Grade;
            }
            averageGrade /= populationSize;
        }

        //run an iteration of the genetic algorithm and update the gardes
        public void performCycle()
        {
            rouletteWheelSelection();
            iterateCrossovers();
            iterateMutations();
            solutions = newSols;
            updateGrades();
        }

        //randomlly select each solution in accordance to its grade - the higher it is the better chance of getting selected
        private void rouletteWheelSelection()
        {
            newSols = new Solution[populationSize];
            newIndex = 0;
            Double[] prob = new Double[populationSize];
            int sum = 0;
            for (int i = 0; i < populationSize; i++)
                sum += solutions[i].Grade;  //get some of all grades
            for (int i = 0; i < populationSize; i++)
            {
                int solGrade = solutions[i].Grade;  //give worst solutions the smallest chance to be selected
                if (solGrade == 0)
                    solGrade = 1;
                prob[i] = solGrade / (double)sum;   //calculate the relative probability for each solution (how well it did relativally to the entire population)
            }
            double r;
            newSols[newIndex] = bestSolution;
            ++newIndex;
            while (newIndex < populationSize)   //untill the full population size is reaches
            {
                int curIndex = newIndex;
                for (int i = 0; newIndex< populationSize && i < populationSize; i++)
                {
                    if (prob[i] > rand.NextDouble())
                    {
                        newSols[newIndex] = solutions[i];
                        ++newIndex;
                    }
                    if (curIndex == newIndex)   //if we didn't add at least one solution reset the random variable
                        r = rand.NextDouble();
                }
            }
            solutions = newSols;
        }

        //run over the entire population and cross each 2 pairs if they get a lower score than the rate
        private void iterateCrossovers()
        {
            for (int i = 0; i < populationSize; i += 2)
            {	// for each pair of chromosoms
                if (i + 1 >= populationSize)
                    break;
                else if (rand.NextDouble() <= Pc)
                {	// then crossover
                    Solution[] crossedSols = solutions[i].crossover(solutions[i + 1]);
                    solutions[i] = crossedSols[0];
                    solutions[i + 1] = crossedSols[1];
                }
            }
        }

        //run over the entire population and mutate each one if it gets a lower score than the rate
        private void iterateMutations()
        {
            for (int i = 0; i < populationSize; i++)
            {
                if (rand.NextDouble() <= Pm)
                    solutions[i].mutate();
            }
        }
    }
}
